package com.zengqingfa.zookeeper.demo.features;

import java.util.concurrent.CountDownLatch;

import com.zengqingfa.zookeeper.demo.watcher.ZkConnectionWatcher;
import org.apache.commons.lang3.StringUtils;
import org.apache.zookeeper.*;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.data.Stat;
import org.junit.Test;

/**
 * 参考：https://segmentfault.com/a/1190000021551262
 * 实现方式有两种,一种通过节点,一种通过节点的版本号
 *
 * 方式一：节点的特性
 * 持久顺序节点(PERSISTENT_SEQUENTIAL)
 * 他的基本特性和持久节点是一致的，额外的特性表现在顺序性上。在ZooKeeper中，每个父节点都会为他的第一级子节点
 * 维护一份顺序，用于记录下每个子节点创建的先后顺序。基于这个顺序特性，在创建子节点的时候，可以设置这个标记，
 * 那么在创建节点过程中，ZooKeeper会自动为给定节点加上一个数字后缀，作为一个新的、完整的节点名。另外需要注
 * 意的是，这个数字后缀的上限是整型的最大值。
 *
 * 方式二：版本-保证分布式数据原子性操作
 * ZooKeeper中为数据节点引入了版本的概念，每个数据节点都具有三种类型的版本信息，对数据节点的任何更新操作都会
 * 引起版本号的变化。
 */
public class GloballyUniqueId implements Watcher {
    //  zk的连接串
    String IP = "localhost:2181";
    //  计数器对象
    CountDownLatch countDownLatch = new CountDownLatch(1);
    //  用户生成序号的节点
    String defaultPath = "/gen-id";
    //  用户根据版本号生成
    String versionPath = "/gen-version-id";
    //  连接对象
    ZooKeeper zooKeeper;

    @Override
    public void process(WatchedEvent event) {
        try {
            // 捕获事件状态
            if (event.getType() == Event.EventType.None) {
                if (event.getState() == KeeperState.SyncConnected) {
                    System.out.println("连接成功");
                    countDownLatch.countDown();
                } else if (event.getState() == KeeperState.Disconnected) {
                    System.out.println("连接断开!");
                } else if (event.getState() == KeeperState.Expired) {
                    System.out.println("连接超时!");
                    // 超时后服务器端已经将连接释放，需要重新连接服务器端
                    zooKeeper = new ZooKeeper(IP, 6000, new ZkConnectionWatcher());
                } else if (event.getState() == KeeperState.AuthFailed) {
                    System.out.println("验证失败!");
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    // 构造方法
    public GloballyUniqueId() {
        try {
            //打开连接
            zooKeeper = new ZooKeeper(IP, 50000, this);
            // zooKeeper.create(defaultPath, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL);
            // 阻塞线程，等待连接的创建成功
            countDownLatch.await();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     * 根据顺序节点
     * @return
     */
    public String getUniqueId() {
        String path = "";
        try {
            //创建持久化顺序节点
            path = zooKeeper.create(defaultPath, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL);
            Long id = Long.valueOf(path.substring(defaultPath.length())) - 1L;
            String leftPad = StringUtils.leftPad(id.toString(), path.length() - defaultPath.length(), "0");
            //删除顺序节点
            Stat stat = zooKeeper.exists(defaultPath + leftPad, false);
            if (stat != null) {
                zooKeeper.delete(defaultPath + leftPad, -1);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return path.substring(defaultPath.length());
    }

    /**
     *  根据版本号生成
     * @return
     */
    public long next() {
        try {
            //删除顺序节点
            Stat statExist = zooKeeper.exists(versionPath, false);
            if (statExist == null) {
                zooKeeper.create(versionPath, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            }
            Stat stat = zooKeeper.setData(versionPath, new byte[0], -1);
            return stat.getVersion();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return -1;
    }

    @Test
    public void test1() {
        GloballyUniqueId globallyUniqueId = new GloballyUniqueId();
        System.out.println("根据顺序节点生成=================");
        for (int i = 1; i <= 5; i++) {
            String id = globallyUniqueId.getUniqueId();
            System.out.println(id);
        }
    }

    @Test
    public void test2() {
        GloballyUniqueId globallyUniqueId = new GloballyUniqueId();
        System.out.println("根据版本号生成=================");
        for (int j = 0; j < 5; j++) {
            long next = globallyUniqueId.next();
            System.out.println(next);
        }
    }
}